/******************************************************************************
 *  _____     _____
 *  \  __\    \  __\
 *   \ \_\\    \ \_\\
 *    \  __\SS  \  __\roject
 *     \_\       \_\
 *
 * Mateusz Maciąg, Rafał Urbański
 * Komputerowe Systemy Sterowania
 *****************************************************************************/

#ifndef WAVEFORMBASE_H
#define WAVEFORMBASE_H

#include "SimInterface/IConfigurable.h"
#include "SimInterface/ISimulator.h"

#include <boost/smart_ptr.hpp>

// Przestrzeń nazw
namespace RafMat
{
namespace SimEngine
{

// Deklaracja zapowiadająca.
class WaveformBase;

/** Alias na typ zręcznego wskaźnika dla obiektów WaveformBase. */
typedef boost::shared_ptr<WaveformBase> WaveformBaseSPtr;

/**
    Klasa bazowa generatorów sygnałów wejściowych.

    Klasa zawiera ogólny interfejs sygnałów. Dziedziczące klasy
    mają za zadanie zaimplementować metodę getNextValue zwracającą
    wartości charakterystyczne dla danego rodzaju sygnału.

    Klasa zakłada ogólne parametry takie jak wzmocnienie sygnału,
    okres, przesunięcie w pionie, oraz przesunięcie fazowe. Klasy
    potomne nie muszą korzystać z tych parametrów, mogą także
    przedefiniować i zastosować w innym celu (np. okres można wykorzystać
    jako moment włączenia sygnału skoku jednostkowego).

    Klasa implementuje interfejs IConfigurable, oraz rozpoznaje domyślnie
    poniższy zestaw parametrów:
    - \c gain
    - \c period
    - \c offset
    - \c phase

    Klasy potomne, jeśli definiują własną implementację interfejsu IConfigurable
    i chcą być czułe na powyższe komendy powinny wywoływać metodę z klasy bazowej:

    \code
    if(param == ...)
        ...
    else if(param == ...)
        ...
    else
        return WaveformBase::setParameter(...);
    \endcode

    \todo Dodać klauzule throw() dla metod get/set, i zmienić protected na private dla zmiennych m_*.
    Kompilator powinien wtedy zoptymalizować pobór wartości z klas bazowych przez metody.
*/
class WaveformBase : public SimInterface::IConfigurable
{
public:
    WaveformBase(SimInterface::ISimulator& simulator, std::string type,
                 double gain = 1, double period = 25,
                 double offset = 0, double phase = 0);

    WaveformBase(SimInterface::ISimulator& simulator, std::string name, std::string type,
                 double gain = 1, double period = 25,
                 double offset = 0, double phase = 0);

    /**
        Przypisuje określonemu parametrowi pewną wartość.
        Klasa implementująca ma za zadanie rozróżnić znaczenie parametrów
        oraz zasygnalizować ewentualny błąd użytkownika za pomocą wyjątku std::invalid_argument.

        \param [in] param parametr, do którego zapisujemy.
        \param [in] value wartość, którą zapisujemy (w formie łańcucha znaków).
        \throws std::invalid_argument - gdy parametr lub wartość nie zgadza się
        syntaktycznie z daną implementacją.
    */
    virtual bool setParameter(const std::string& param, const std::string& value) throw(std::logic_error);

    /**
        Odczytuje określony parametr.
        Klasa implementująca ma za zadanie rozróżnić czego od niej się chce
        oraz zasygnalizować ewentualny błąd parametru.

        \param [in] param parametr, który chcemy odczytać.
        \return Wartość odpowiadająca parametrowi w formie łańcucha znaków.
        \throws std::invalid_argument - gdy parametr nie zgadza się
        syntaktycznie z daną implementacją.
    */
    virtual std::string getParameter(const std::string& param) const throw(std::logic_error);

    /**
        Wysyła stan obiektu do strumienia.

        \param [in/out] o strumień docelowy.
        \return Strumień przekazany funkcji.
    */
    virtual SimFile::ConfigWriter& dumpParameters(SimFile::ConfigWriter& o) const;

    /**
        Zwraca aktualną wartość sygnału.
        Metoda implementowana przez klasy potomne.
        \return Wartość sygnału.
    */
    virtual double getNextValue() = 0;

    /**
        Ustawia wzmocnienie sygnału.
        \param [in] gain Wzmocnienie.
    */
    void setGain(double gain) { m_gain = gain; }

    /**
        Ustawia okres sygnału.
        \param [in] period Okres sygnału.
    */
    void setPeriod(double period) { m_period = period; }

    /**
        Ustawia przesunięcie w pionie sygnału.
        \param [in] offset Wartość przesunięcia.
    */
    void setOffset(double offset) { m_offset = offset; }

    /**
        Ustawia przesunięcie fazowe sygnału.
        \param [in] phase Wartość przesunięcia (znormalizowana).
    */
    void setPhase(double phase) { m_phase = phase; }

    /**
        Zwraca wzmocnienie sygnału.
    */
    double getGain() const { return m_gain; }

    /**
        Zwraca okres sygnału.
    */
    double getPeriod() const { return m_period; }

    /**
        Zwraca przesunięcie pionowe sygnału.
    */
    double getOffset() const { return m_offset; }

    /**
        Zwraca przesunięcie fazowe sygnału.
    */
    double getPhase() const { return m_phase; }

protected:
    /**
        Zwraca aktualny czas symulacji.
        Czas brany jest na podstawie stanu obiektu symulatora.
        Metoda powinna być wykorzystywana przez klasy potomne przy definiowaniu
        funkcji opisujących sygnał.

        \return Aktualny czas w próbkach.
    */
    unsigned int getCurrentTime() const { return m_simulator.currentSampleNum(); }

    /**
        Wrzuca do konfiguracji parametry specyficzne dla danego obiektu.
    */
    virtual SimFile::ConfigWriter& dumpSpecificPar(SimFile::ConfigWriter& o) const;

protected:

    // Stałe pomocnicze dla interfejsu IConfigurable.
    static const std::string CONF_TYPE;
    static const std::string CONF_GAIN;
    static const std::string CONF_PERIOD;
    static const std::string CONF_OFFSET;
    static const std::string CONF_PHASE;

private:

    SimInterface::ISimulator& m_simulator;      ///< Instancja symulatora-właściciela.
    std::string m_type; ///< Typ wejścia.

    double m_gain;      ///< Wzmocnienie sygnału (np. do określenia amplitudy)
    double m_period;    ///< Okres sygnału
    double m_offset;    ///< Przesunięcie sygnału w pionie
    double m_phase;     ///< Przesunięcie fazowe sygnału
};


}   // SimEngine namespace
}   // RafMat namespace

#endif // WAVEFORMBASE_H
